﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Research.Kinect.Nui;

//for kinect to flash
using System.Net.Sockets;
using System.Net;
using System.IO;
using Coding4Fun.Kinect.Wpf;
//end

namespace IntelBillboard
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        //for kinect to flash
        Socket sock;
        Microsoft.Research.Kinect.Nui.Vector pos;
        bool firstTimeDetectUser = true;
        bool displayUserSelection = false;
        bool checkUserType = false;
        const int DISPLAY_IDLE_AD = 96;
        const int FEMALE_SELECTED = 97;
        const int MALE_SELECTED = 98;
        const int CHILDREN_SELECTED = 99;
        const int DISPLAY_USER_SELECTION = 100;
        const int TRAN_CALIBRATION = 101;
        const int TRAN_LEFTUP = 102;
        const int TRAN_RIGHTUP = 103;
        const int FOOTBALL_GOLEFT = 104;
        const int FOOTBALL_GORIGHT = 105;
        const int FOOTBALL_KICK = 106;
        const int SLIDER_SWAP_RIGHT = 107;
        const int SLIDER_SWAP_LEFT = 108;
        const int SLIDER_SELECT_TRANSFORMER = 109;
        const int SLIDER_SELECT_FOOTBALL = 110;
        const int SLIDER_SELECT = 111;
        //end
        int indexForChange = 0;


        Runtime nui;
        DateTime lastTime = DateTime.MaxValue;

        // For updating skeleton history to track dynamic gestures
        List<SkeletonData> skeletonHistory = new List<SkeletonData>();
        // Number of history frames tracked
        const int SKELETON_HISTORY_COUNT = 30;

        GestureController gc = new GestureController();
        UIController uc = new UIController();


        Dictionary<JointID, Brush> jointColors = new Dictionary<JointID, Brush>() { 
            {JointID.HipCenter, new SolidColorBrush(Color.FromRgb(169, 176, 155))},
            {JointID.Spine, new SolidColorBrush(Color.FromRgb(169, 176, 155))},
            {JointID.ShoulderCenter, new SolidColorBrush(Color.FromRgb(168, 230, 29))},
            {JointID.Head, new SolidColorBrush(Color.FromRgb(200, 0,   0))},
            {JointID.ShoulderLeft, new SolidColorBrush(Color.FromRgb(79,  84,  33))},
            {JointID.ElbowLeft, new SolidColorBrush(Color.FromRgb(84,  33,  42))},
            {JointID.WristLeft, new SolidColorBrush(Color.FromRgb(255, 126, 0))},
            {JointID.HandLeft, new SolidColorBrush(Color.FromRgb(215,  86, 0))},
            {JointID.ShoulderRight, new SolidColorBrush(Color.FromRgb(33,  79,  84))},
            {JointID.ElbowRight, new SolidColorBrush(Color.FromRgb(33,  33,  84))},
            {JointID.WristRight, new SolidColorBrush(Color.FromRgb(77,  109, 243))},
            {JointID.HandRight, new SolidColorBrush(Color.FromRgb(37,   69, 243))},
            {JointID.HipLeft, new SolidColorBrush(Color.FromRgb(77,  109, 243))},
            {JointID.KneeLeft, new SolidColorBrush(Color.FromRgb(69,  33,  84))},
            {JointID.AnkleLeft, new SolidColorBrush(Color.FromRgb(229, 170, 122))},
            {JointID.FootLeft, new SolidColorBrush(Color.FromRgb(255, 126, 0))},
            {JointID.HipRight, new SolidColorBrush(Color.FromRgb(181, 165, 213))},
            {JointID.KneeRight, new SolidColorBrush(Color.FromRgb(71, 222,  76))},
            {JointID.AnkleRight, new SolidColorBrush(Color.FromRgb(245, 228, 156))},
            {JointID.FootRight, new SolidColorBrush(Color.FromRgb(77,  109, 243))}
        };

        private void Window_Loaded(object sender, EventArgs e)
        {
            nui = new Runtime();

            //for kinect to flash
            var parameters = new TransformSmoothParameters
            {
                Smoothing = 0.00001f,
                Correction = 0.00001f,
                Prediction = 0.00001f,
                JitterRadius = 0.00001f,
                MaxDeviationRadius = 0.00001f
            };
            //end


            try
            {
                nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor);
                //for kinect to flash
                nui.SkeletonEngine.TransformSmooth = true;
                //end
            }
            catch (InvalidOperationException)
            {
                System.Windows.MessageBox.Show("Runtime initialization failed. Please make sure Kinect device is plugged in.");
                return;
            }


            try
            {
                nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
                nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);
            }
            catch (InvalidOperationException)
            {
                System.Windows.MessageBox.Show("Failed to open stream. Please make sure to specify a supported image type and resolution.");
                return;
            }

            lastTime = DateTime.Now;

            nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady);

            //for flash
            nui.SkeletonEngine.SmoothParameters = parameters;
            // socket
            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001);
            sock = new Socket(ipEnd.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            sock.Connect("127.0.0.1", 9001);  
            //end


        }

        void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            SkeletonFrame skeletonFrame = e.SkeletonFrame;
            int iSkeleton = 0;
            Brush[] brushes = new Brush[6];
            brushes[0] = new SolidColorBrush(Color.FromRgb(255, 0, 0));
            brushes[1] = new SolidColorBrush(Color.FromRgb(0, 255, 0));
            brushes[2] = new SolidColorBrush(Color.FromRgb(64, 255, 255));
            brushes[3] = new SolidColorBrush(Color.FromRgb(255, 255, 64));
            brushes[4] = new SolidColorBrush(Color.FromRgb(255, 64, 255));
            brushes[5] = new SolidColorBrush(Color.FromRgb(128, 128, 255));

            int noOfUser = 0;
            skeleton.Children.Clear();
            foreach (SkeletonData data in skeletonFrame.Skeletons)
            {
                if (SkeletonTrackingState.Tracked == data.TrackingState)
                {
                    noOfUser++;
                    //Console.WriteLine(noOfUser);
                   

                    // Draw bones
                    Brush brush = brushes[iSkeleton % brushes.Length];
                    skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.Spine, JointID.ShoulderCenter, JointID.Head));
                    skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.ShoulderCenter, JointID.ShoulderLeft, JointID.ElbowLeft, JointID.WristLeft, JointID.HandLeft));
                    skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.ShoulderCenter, JointID.ShoulderRight, JointID.ElbowRight, JointID.WristRight, JointID.HandRight));
                    skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.HipLeft, JointID.KneeLeft, JointID.AnkleLeft, JointID.FootLeft));
                    skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.HipRight, JointID.KneeRight, JointID.AnkleRight, JointID.FootRight));

                    // Draw joints
                    foreach (Joint joint in data.Joints)
                    {
                        Point jointPos = getDisplayPosition(joint);
                        Line jointLine = new Line();
                        jointLine.X1 = jointPos.X - 3;
                        jointLine.X2 = jointLine.X1 + 6;
                        jointLine.Y1 = jointLine.Y2 = jointPos.Y;
                        jointLine.Stroke = jointColors[joint.ID];
                        jointLine.StrokeThickness = 6;
                        skeleton.Children.Add(jointLine);
                    }

                    // Update skeleton history for dynamic gesture
                    UpdateSkeletonHistory(data);




                    if (sock.Connected)
                    {
                        byte[] bytes;
                        int command = 0;
                        int command1 = 0;
                        int command2 = 0;
                        using (var ms = new MemoryStream())
                        using (var bw = new BinaryWriter(ms))
                        {

                            if (firstTimeDetectUser)
                            {
                                Console.WriteLine("First time detect user!");
                                //ask flash to display user selection screen       


                                bw.Write(DISPLAY_USER_SELECTION);
                                Console.WriteLine(DISPLAY_USER_SELECTION);                                
                                bytes = ms.ToArray();
                                sock.Send(bytes);
                                firstTimeDetectUser = false;
                                checkUserType = true;
                                //checkUserType = false;
                                
                            }

                            if(checkUserType)
                            {
                                if (gc.checkMale(data.Joints))
                                {
                                    Console.WriteLine("Male detected");
                                    command1 = MALE_SELECTED;
                                    checkUserType = false;

                                    uc.setCounter(10);
                                    uc.setCurrentUI();
                                }
                                else if (gc.checkFemale(data.Joints))
                                {
                                    Console.WriteLine("Female detected");
                                    command1 = FEMALE_SELECTED;
                                    checkUserType = false;

                                    uc.setCounter(7);
                                    uc.setCurrentUI();
                                }
                                else if (gc.checkChildren(skeletonHistory))
                                {
                                    Console.WriteLine("Children detected");
                                    command1 = CHILDREN_SELECTED;
                                    checkUserType = false;

                                    uc.setCounter(4);
                                    uc.setCurrentUI();
                                }

                                //Send detection info
                                if (command1 != 0)
                                {
                                    bw.Write(command1);
                                    Console.WriteLine(command1);
                                    command1 = 0;
                                    bytes = ms.ToArray();
                                    sock.Send(bytes);
                                }
                            }

                            if (!checkUserType)
                            {

                                //For Transformer UI
                                if (uc.getCurrentUI().getUseCalibration() && gc.checkCalibration(data.Joints))
                                {
                                    Console.WriteLine("calibration gesture detected");
                                    command = TRAN_CALIBRATION;
                                    UICase_Transformer ui_t = (UICase_Transformer)uc.getCurrentUI();
                                    ui_t.waitForLeftHand();
                                }

                                else if (uc.getCurrentUI().getUseLeftHandUp() && gc.checkLeftHandUp(skeletonHistory))
                                {
                                    Console.WriteLine("LeftthandUp");
                                    command = TRAN_LEFTUP;
                                    UICase_Transformer ui_t = (UICase_Transformer)uc.getCurrentUI();
                                    ui_t.waitForRightHand();
                                }

                                else if (uc.getCurrentUI().getUseRightHandUp() && gc.checkRightHandUp(skeletonHistory))
                                {
                                    Console.WriteLine("RighthandUp");
                                    command = TRAN_RIGHTUP;
                                    UICase_Transformer ui_t = (UICase_Transformer)uc.getCurrentUI();
                                    ui_t.reset();
                                }

                                //For football UI
                                else if (uc.getCurrentUI().getUsePlayerPositionAtLeft() && gc.checkMoveToLeft(skeletonHistory))
                                {
                                    Console.WriteLine("position at left");
                                    command = FOOTBALL_GOLEFT;
                                    UICase_Football ui_f = (UICase_Football)uc.getCurrentUI();
                                    ui_f.movedToLeft();
                                }
                                else if (uc.getCurrentUI().getUsePlayerPositionAtRight() && gc.checkMoveToRight(skeletonHistory))
                                {
                                    Console.WriteLine("position at right");
                                    command = FOOTBALL_GORIGHT;
                                    UICase_Football ui_f = (UICase_Football)uc.getCurrentUI();
                                    ui_f.movedToRight();
                                }

                                else if (uc.getCurrentUI().getUseRightKicking() && gc.checkRightKicking(skeletonHistory))
                                {
                                    Console.WriteLine("kick right");
                                    command = FOOTBALL_KICK;
                                    UICase_Football ui_f = (UICase_Football)uc.getCurrentUI();
                                    ui_f.finishKicking();
                                }

                                //Send first time for UI case command
                                if (command != 0)
                                {
                                    bw.Write(command);
                                    Console.WriteLine(command);
                                    command = 0;
                                    bytes = ms.ToArray();
                                    sock.Send(bytes);
                                }


                                //For menu bar
                                if (uc.getMenuBar().getUseSelect() && (gc.checkSelectPushRight(skeletonHistory) || gc.checkSelectPushLeft(skeletonHistory)))
                                {
                                    Console.WriteLine("select detected");
                                    if (uc.getCounter() == 4)
                                    {
                                        Console.WriteLine("Transformer selected.");
                                        command2 = SLIDER_SELECT_TRANSFORMER;
                                        uc.setCurrentUI();
                                    }
                                    else if (uc.getCounter() == 10)
                                    {
                                        Console.WriteLine("Football selected");
                                        command2 = SLIDER_SELECT_FOOTBALL;
                                        uc.setCurrentUI();
                                    }
                                    else
                                    {
                                        Console.WriteLine("Static UI case selected. Current UI index is: " + uc.getCounter());
                                        command2 = SLIDER_SELECT;
                                        uc.setCurrentUI();
                                    }

                                }

                                else if (uc.getMenuBar().getUseSwapLeft() && gc.checkSwapLeft(skeletonHistory))
                                {
                                    Console.WriteLine("swap left detected");
                                    uc.increaseCounter();
                                    command2 = SLIDER_SWAP_LEFT;
                                }

                                else if (uc.getMenuBar().getUseSwapRight() && gc.checkSwapRight(skeletonHistory))
                                {
                                    Console.WriteLine("swap right detected");
                                    uc.decreaseCounter();
                                    command2 = SLIDER_SWAP_RIGHT;
                                }


                                //Send second time for UI case command
                                if (command2 != 0)
                                {
                                    bw.Write(command2);
                                    Console.WriteLine(command2);
                                    command2 = 0;
                                    bytes = ms.ToArray();
                                    sock.Send(bytes);
                                }
                            }

                        }
                    }

                }
                iSkeleton++;
            } // for each skeleton

            if (!firstTimeDetectUser && noOfUser == 0)
            {
                Console.WriteLine("User left screen");
                firstTimeDetectUser = true;

                if (sock.Connected)
                {
                    byte[] bytes;
                    using (var ms = new MemoryStream())
                    using (var bw = new BinaryWriter(ms))
                    {
                        bw.Write(DISPLAY_IDLE_AD);
                        Console.WriteLine(DISPLAY_IDLE_AD);
                        bytes = ms.ToArray();
                        sock.Send(bytes);
                    }
                }
            }
        }

        private void UpdateSkeletonHistory(SkeletonData latestSkeletonData)
        {          
            //if(checkSkeletonDataFrames(latestSkeletonData))
                skeletonHistory.Add(latestSkeletonData);
            /*
            // checking 
            Microsoft.Research.Kinect.Nui.Vector footLeft = latestSkeletonData.Joints[JointID.FootLeft].Position;
            Console.WriteLine("User left foot: X-" + footLeft.X + " Y-" + footLeft.Y + " Z-" + footLeft.Z + "\n");
            Microsoft.Research.Kinect.Nui.Vector handRight = latestSkeletonData.Joints[JointID.HandRight].Position;
            Console.WriteLine("User Hand Right: X-" + handRight.X + " Y-" + handRight.Y + " Z-" + handRight.Z + "\n");
             */
             
           

            if (skeletonHistory.Count > SKELETON_HISTORY_COUNT)
            {
                skeletonHistory.RemoveAt(0);
            }
        }




        // Used for cleaning of the sudden weird frames
        

        private bool checkSkeletonDataFrames(SkeletonData latestSkeletonData)
        {

            const float DELTA = 0.2f;

            if (skeletonHistory.Count != 0)
            {
               
                // Checking for Ankles

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.AnkleLeft].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.AnkleLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.AnkleRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.AnkleRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;



                // Checking for Foot

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.FootLeft].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.FootLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.FootRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.FootRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;


                // Checking for Knees

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.KneeLeft].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.KneeLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.KneeRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.KneeRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;



                // Checking for Hips

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.HipLeft].Position,
                   skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.HipLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.HipRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.HipRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.HipCenter].Position,
                skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.HipCenter].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;



                // Checking for Hand

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.HandLeft].Position,
                 skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.HandLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.HandRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.HandRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;



                // Checking for Wrists


                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.WristLeft].Position,
                  skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.WristLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.WristRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.WristRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;


                // Checking for Elbow


                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.ElbowLeft].Position,
                  skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.ElbowLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.ElbowRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.ElbowRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;



                // Checking for Shoulder


                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.ShoulderLeft].Position,
                skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.ShoulderLeft].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.ShoulderRight].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.ShoulderRight].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.ShoulderCenter].Position,
                skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.ShoulderCenter].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;



                // Checking for Head


                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.Head].Position,
                skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.Head].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;

                // Checking for Spine

                if (gc.isApproxEqualVector2(latestSkeletonData.Joints[JointID.Spine].Position,
                    skeletonHistory[skeletonHistory.Count - 1].Joints[JointID.Spine].Position, DELTA))
                {
                    //does nothing
                }
                else
                    return false;


                // if all cases are ran through the joints and stay true 

                return true;

            }

            else
            {
                // if the count is 0 this is the first frame therefore return true
                return true;
            }

            
        }








        private Point getDisplayPosition(Joint joint)
        {
            float depthX, depthY;
            nui.SkeletonEngine.SkeletonToDepthImage(joint.Position, out depthX, out depthY);
            depthX = Math.Max(0, Math.Min(depthX * 320, 320));  //convert to 320, 240 space
            depthY = Math.Max(0, Math.Min(depthY * 240, 240));  //convert to 320, 240 space
            int colorX, colorY;
            ImageViewArea iv = new ImageViewArea();
            // only ImageResolution.Resolution640x480 is supported at this point
            nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, iv, (int)depthX, (int)depthY, (short)0, out colorX, out colorY);

            // map back to skeleton.Width & skeleton.Height
            return new Point((int)(skeleton.Width * colorX / 640.0), (int)(skeleton.Height * colorY / 480));
        }

        Polyline getBodySegment(Microsoft.Research.Kinect.Nui.JointsCollection joints, Brush brush, params JointID[] ids)
        {
            PointCollection points = new PointCollection(ids.Length);
            for (int i = 0; i < ids.Length; ++i)
            {
                points.Add(getDisplayPosition(joints[ids[i]]));
            }

            Polyline polyline = new Polyline();
            polyline.Points = points;
            polyline.Stroke = brush;
            polyline.StrokeThickness = 5;
            return polyline;
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            //nui.Uninitialize();
            Environment.Exit(0);
        }
    }
}
